package wci.util;

import java.io.PrintStream;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import wci.intermediate.ICode;
import wci.intermediate.ICodeKey;
import wci.intermediate.ICodeNode;
import wci.intermediate.SymTabEntry;
import wci.intermediate.icodeimpl.ICodeNodeImpl;

/**
 * <h1>ParseTreePrinter</h1>
 *
 * <p>以XML格式打印分析树</p>
 *
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class ParseTreePrinter
{
    private static final int INDENT_WIDTH = 4;
    private static final int LINE_WIDTH = 80;

    private PrintStream ps;      // 如System.out
    private int length;          // 每行长度，多了就换行wrap
    private final String indent; // 每缩进空格
    private String indentation;  // 行缩进
    private StringBuilder line;  // 行输出内容
    
    public ParseTreePrinter(PrintStream ps)
    {
        this.ps = ps;
        this.length = 0;
        this.indentation = "";
        this.line = new StringBuilder();

        char[] indent_chars = new char[INDENT_WIDTH];
        Arrays.fill(indent_chars, ' ');
        this.indent = String.valueOf(indent_chars);
    }

    /**
     * 打印分析树格式的中间码
     * @param iCode 中间码
     */
    public void print(ICode iCode)
    {
        ps.println("\n======== 中间码  ========\n");

        printNode((ICodeNodeImpl) iCode.getRoot());
        printLine();
    }

    /**
     * 打印树
     * @param node the parse tree node.
     */
    private void printNode(ICodeNodeImpl node)
    {
        // 开始标记
        append(indentation); append("<" + node.toString());

        printAttributes(node);
        printTypeSpec(node);

        List<ICodeNode> childNodes = node.getChildren();

        // 打印所有子节点
        if ((childNodes != null) && (childNodes.size() > 0)) {
            append(">");
            printLine();

            printChildNodes(childNodes);
            append(indentation); append("</" + node.toString() + ">");
        }

        // 没有子节点，直接使用<node/>格式
        else {
            append(" "); append("/>");
        }

        printLine();
    }

    /**
     * 打印节点所有属性
     * @param node 某一个节点
     */
    private void printAttributes(ICodeNodeImpl node)
    {
        String saveIndentation = indentation; //根本用不上？？
        indentation += indent;

        Set<Map.Entry<ICodeKey, Object>> attributes = node.entrySet();
        Iterator<Map.Entry<ICodeKey, Object>> it = attributes.iterator();

        while (it.hasNext()) {
            Map.Entry<ICodeKey, Object> attribute = it.next();
            printAttribute(attribute.getKey().toString(), attribute.getValue());
        }

        indentation = saveIndentation;
    }

    /**
     * 打印节点的某一属性
     * @param keyString 属性名
     * @param value the 属性值
     */
    private void printAttribute(String keyString, Object value)
    {
        // 如果值是符号表项，则使用表项的名字
        boolean isSymTabEntry = value instanceof SymTabEntry;
        String valueString = isSymTabEntry ? ((SymTabEntry) value).getName()
                                           : value.toString();

        String text = keyString.toLowerCase() + "=\"" + valueString + "\"";
        append(" "); append(text);

        //如果是表项，将其嵌套层级加上
        if (isSymTabEntry) {
            int level = ((SymTabEntry) value).getSymTab().getNestingLevel();
            printAttribute("LEVEL", level);
        }
    }

    /**
     * 打印节点的所有子节点
     * @param childNodes 子节点列表
     */
    private void printChildNodes(List<ICodeNode> childNodes)
    {
        String saveIndentation = indentation;
        indentation += indent;

        for (ICodeNode child : childNodes) {
            printNode((ICodeNodeImpl) child);
        }

        indentation = saveIndentation;
    }

    /**
     * 打印节点类型，第10才实现，目前占位
     * @param node 某一节点
     */
    private void printTypeSpec(ICodeNodeImpl node)
    {
    }

    /**
     * 附加文本到行缓冲
     * @param text 要附加的文本
     */
    private void append(String text)
    {
        int textLength = text.length();
        boolean lineBreak = false;

        // 换如果太长换行，wrap折回
        if (length + textLength > LINE_WIDTH) {
            printLine();
            line.append(indentation);
            length = indentation.length();
            lineBreak = true;
        }

        if (!(lineBreak && text.equals(" "))) {
            line.append(text);
            length += textLength;
        }
    }

    /**
     * 输出行内容，并清空buffer中数据。
     */
    private void printLine()
    {
        if (length > 0) {
            ps.println(line);
            line.setLength(0);
            length = 0;
        }
    }
}
